// Copyright (c) 2019 Presto Labs Pte. Ltd.
// Author: donggu
use strum_macros::Display;
use failure::Error;
use crate::currency::Currency;
use crate::date::{self, Date};
use crate::exchange::{ExchangeEnum, Exchange};

pub enum ContractType {
    Spot,
    Futures{expiry_type: ExpiryType},
    Perpetual,
}

pub enum Contract {
    Spot,
    Futures{expiry: Expiry},
    Perpetual,
}

impl std::fmt::Display for Contract {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Contract::Spot => write!(f, "Spot"),
            Contract::Futures{expiry} => write!(f, "Futures_{}", expiry),
            Contract::Perpetual => write!(f, "Perpetual"),
        }
        
    }
}

#[derive(Copy, Clone, Display)]
pub enum ExpiryType {
    Weekly,
    Monthly,
    Quarterly,
}

pub struct Expiry {
    expiry_type: ExpiryType,
    date: Date,
}

impl Expiry {
    /// Expiry::new(type: ExpiryType::ThisWeek, date: Date::today())
    fn new_exact<E:Exchange>(expiry_type: ExpiryType, expiry_date: &Date) -> Result<Expiry, Error> {
        // TODO: validate expiry_date
        Ok(Expiry {
            expiry_type,
            date: *expiry_date,
        })
    }
    fn new<E:Exchange>(expiry_type: ExpiryType, as_of: &Date) -> Result<Expiry, Error> {
        // TODO: validate if expiry_type exists in exchange
        Ok(Expiry {
            expiry_type,
            date: E::expiry_date(expiry_type, as_of),
        })
    }
    fn expired(&self, date: &Date) -> bool {
        return &self.date <= date;
    }
}

impl std::fmt::Display for Expiry {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}_{}", self.expiry_type, date::to_yymmdd(&self.date))
    }
}

pub struct Product {
    exchange: ExchangeEnum,
    base: Currency,
    quote: Currency,
    contract: Contract,
}

impl Product {
    fn new(exchange: ExchangeEnum, base: Currency, quote: Currency, contract: Contract) -> Result<Product, Error> {
        // TODO: verify
        Ok(Product {
            exchange,
            base,
            quote,
            contract,
        })
    }
    // exchange-specific symbol
    fn native_symbol() -> String {
        unimplemented!();
    }

    /// unique identifier across exchanges (only used in rust)
    fn global_symbol(&self) -> String {
        format!("{}/{}-{}/{}", self.exchange, self.base, self.quote, self.contract);
        unimplemented!();
    }
}
